Skip to content

Conversation

@wizard-ci-bot
Copy link

@wizard-ci-bot wizard-ci-bot bot commented Jan 15, 2026

Automated wizard CI run

Source: manual
Trigger ID: a283762
App: next-js/15-app-router-saas
App directory: apps/next-js/15-app-router-saas
Workbench branch: wizard-ci-a283762-next-js-15-app-router-saas
Wizard branch: main
Examples branch: main
PostHog (MCP) branch: master
Timestamp: 2026-01-15T22:38:44.864Z
Duration: 405.5s

@wizard-ci-bot
Copy link
Author

wizard-ci-bot bot commented Jan 15, 2026

The files are not available in the local file system as this is a PR evaluation. I'll proceed to evaluate the PR based on the diff provided.


PR Evaluation Report

Summary

This PR integrates PostHog analytics into a Next.js 15 App Router SaaS application. It adds both client-side (posthog-js) and server-side (posthog-node) tracking, implements a reverse proxy for ad-blocker circumvention, and captures events across authentication, subscription management, and team management flows.

Files changed Lines added Lines removed
14 +589 -9

Confidence score: 4/5 👍

  • User identification uses email as distinct ID: Using email directly as the distinct ID can cause issues when users change their email. Consider using a stable user ID instead. [MEDIUM]
  • onClick event timing issue: Several posthog.capture() calls are attached to onClick handlers on form submit buttons. These fire before form validation and before the action succeeds, potentially capturing events that fail. [MEDIUM]
  • Server-side client singleton with shutdown: The posthog-server.ts creates a singleton but calls shutdown() after each request, which would invalidate the singleton pattern and could cause issues with concurrent requests. [MEDIUM]

File changes

Filename Score Description
instrumentation-client.ts 5/5 New file implementing client-side PostHog initialization using the Next.js 15 instrumentation pattern. Properly configures api_host for reverse proxy, enables exception capture, and sets debug mode for development.
lib/posthog-server.ts 3/5 New file for server-side PostHog client. Implements singleton pattern but the shutdown calls in route handlers conflict with singleton reuse. flushAt: 1, flushInterval: 0 settings ensure immediate flushing.
next.config.ts 5/5 Adds reverse proxy rewrites for PostHog to circumvent ad-blockers. Correctly routes /ingest/static/* and /ingest/* to PostHog's US endpoints.
package.json 5/5 Adds posthog-js@^1.324.1 and posthog-node@^5.21.0 as dependencies.
app/(login)/login.tsx 3/5 Implements user identification and sign-in/sign-up event tracking using useEffect to detect successful auth. Uses email as distinct ID which is not ideal.
app/(dashboard)/layout.tsx 4/5 Adds sign-out event tracking with posthog.reset() call to clear user identity. Properly placed before the sign-out action.
app/(dashboard)/pricing/submit-button.tsx 4/5 Adds checkout_started event on button click. Event fires before Stripe redirect which is acceptable.
app/(dashboard)/dashboard/page.tsx 4/5 Adds team management events (invite, remove, manage subscription). Uses useRef to capture form values.
app/(dashboard)/dashboard/general/page.tsx 3/5 Adds account_updated event on onClick which fires before action completes - may capture failed updates.
app/(dashboard)/dashboard/security/page.tsx 4/5 Adds password_updated and account_deleted events. Account deletion properly calls posthog.reset().
app/api/stripe/checkout/route.ts 4/5 Server-side checkout_completed event with rich properties. Uses email as distinct ID.
app/api/stripe/webhook/route.ts 3/5 Server-side subscription events. Uses Stripe customer ID as distinct ID which differs from email used elsewhere - inconsistent identity.
pnpm-lock.yaml 5/5 Lock file updated with PostHog dependencies and their transitive dependencies.
posthog-setup-report.md 5/5 Comprehensive documentation of the integration with event inventory and next steps.

App sanity check: 4/5 ✅

Criteria Result Description
App builds and runs Yes Dependencies added correctly, imports valid, no syntax errors visible in diff
Preserves existing env vars & configs Yes Only adds to next.config.ts, doesn't remove existing config
No syntax or type errors Yes TypeScript usage appears correct, proper type imports
Correct imports/exports Yes All posthog-js and posthog-node imports are correct
Minimal, focused changes Yes Changes are focused on PostHog integration only

Issues

  • Server-side client singleton pattern conflict: The getPostHogClient() creates a singleton, but shutdown() is called after each request in route handlers. After shutdown, the singleton reference remains but the client is invalidated. This could cause issues with concurrent requests or subsequent requests reusing a shutdown client. Consider either creating a new client per request or using a proper request-scoped pattern. [MEDIUM]

Other completed criteria

  • Clear, readable code following existing patterns
  • Environment variables documented in setup report
  • No secrets or PII hardcoded
  • Proper async/await handling in server routes

PostHog implementation: 4/5 ✅

Criteria Result Description
PostHog SDKs installed Yes posthog-js@^1.324.1 and posthog-node@^5.21.0 added to package.json
PostHog client initialized Yes Client init via instrumentation-client.ts using Next.js 15 pattern; server client via lib/posthog-server.ts
capture() Yes Multiple capture calls across auth, subscription, team, and account events
identify() Yes User identification on sign-in/sign-up with email
Error tracking Yes capture_exceptions: true enabled in client config
Reverse proxy Yes Properly configured in next.config.ts with /ingest rewrites to PostHog US endpoints

Issues

  • Inconsistent distinct ID strategy: Client-side uses email for identify(), server-side checkout uses email, but webhook handler uses Stripe customer ID. This creates fragmented user profiles in PostHog. All events should use a consistent identifier (preferably a stable internal user ID). [MEDIUM]
  • onClick timing captures premature events: Events like account_updated, password_updated, team_member_invited fire on button click before the server action completes. If the action fails, the event is still captured, leading to inaccurate analytics. Consider tracking on successful action completion instead. [MEDIUM]
  • Missing pageview tracking: No explicit pageview tracking setup. While capture_pageview defaults may handle this, it's not explicitly configured. [LOW]

Other completed criteria

  • posthog.reset() called on sign-out and account deletion
  • Debug mode enabled only in development
  • Proper api_host configuration for reverse proxy
  • defaults: '2025-05-24' sets modern SDK defaults
  • Server-side events include relevant business properties

PostHog insights and events: 4/5 ✅

Filename PostHog events Description
login.tsx user_signed_in, user_signed_up, identify() Core authentication funnel tracking with user identification
layout.tsx user_signed_out Session end tracking with identity reset
submit-button.tsx checkout_started Checkout funnel entry point
checkout/route.ts checkout_completed Server-side checkout success with plan details
webhook/route.ts subscription_updated, subscription_cancelled Subscription lifecycle events
dashboard/page.tsx manage_subscription_clicked, team_member_removed, team_member_invited Team and subscription management actions
general/page.tsx account_updated Profile update tracking
security/page.tsx password_updated, account_deleted Security and account lifecycle events
instrumentation-client.ts capturedException Automatic exception capture enabled

Issues

  • Webhook events lack user context: Subscription events use Stripe customer ID but don't link to the internal user identity used elsewhere. This breaks funnel analysis connecting checkout_startedcheckout_completedsubscription_updated. [MEDIUM]
  • Team member events capture PII: team_member_invited captures invited_email which may be considered PII depending on compliance requirements. [LOW]

Other completed criteria

  • Events cover full user lifecycle (signup → subscription → team management → account deletion)
  • Events enriched with relevant properties (plan_name, subscription_status, member_role)
  • Supports building conversion funnels (signup → checkout → subscription)
  • Enables cohort analysis by subscription status
  • Team size and engagement metrics trackable

Reviewed by wizard workbench PR evaluator

@wizard-ci-bot wizard-ci-bot bot added the CI/CD label Jan 15, 2026
@wizard-ci-bot wizard-ci-bot bot closed this Jan 15, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant